﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Exceptions;
using MugenInjection.Infrastructure;
using MugenInjection.Interface.Components;
using MugenInjection.Wcf.Config;
using MugenInjection.Wcf.Interface;
using MugenInjection.Web;
using MugenInjection.Web.Settings;
using Should;

namespace MugenInjectionTest.Web.Wcf
{
    [TestClass]
    public class ApplicationConfigurationWcfTest : TestBase
    {
        [TestMethod]
        public void ConfigurationsUseDisposeBehaviorTest()
        {
            using (var injector = GetInjector())
            {
                MugenInjectionHttpApplication.ApplicationConfiguration = new ApplicationConfiguration(injector);
                MugenInjectionHttpApplication.ApplicationConfiguration.WithWcfConfiguration();
                var releaseInstanceBehavior = injector.Get<IReleaseInstanceBehavior>(WcfConfigExtension.ReleaseInstanceKey);
                releaseInstanceBehavior.ShouldBeType<DisposeReleaseInstanceBehavior>();
            }
        }

        [TestMethod]
        public void ConfigurationsNotUseDisposeBehaviorTest()
        {
            using (var injector = GetInjector())
            {
                MugenInjectionHttpApplication.ApplicationConfiguration = new ApplicationConfiguration(injector);
                MugenInjectionHttpApplication.ApplicationConfiguration.WithWcfConfiguration(false);
                TestUtils.Catch<BindingNotFoundException>(
                    () => injector.Get<IReleaseInstanceBehavior>(WcfConfigExtension.ReleaseInstanceKey));
            }
        }

        [TestMethod]
        public void OptionalSettingTest()
        {
            using (var injector = GetInjector())
            {
                var applicationConfiguration = new ApplicationConfiguration(injector);
                MugenInjectionHttpApplication.ApplicationConfiguration = applicationConfiguration;
                var customMessage = new CustomMessageInspector();
                var customMessageMethod = new CustomMessageInspector();
                var customBehavior = new CustomServiceBehavior();
                var customBehaviorMethod = new CustomServiceBehavior();

                applicationConfiguration.WithDispatchMessageInspector(syntax => syntax.ToConstant(customMessage));
                applicationConfiguration.WithDispatchMessageInspector(context => context.ToConstant(customMessageMethod));
                applicationConfiguration.WithServiceBehavior(syntax => syntax.ToConstant(customBehavior));
                applicationConfiguration.WithServiceBehavior(context => context.ToConstant(customBehaviorMethod));

                List<IDispatchMessageInspector> dispatchMessageInspectors = injector
                    .GetAll<IDispatchMessageInspector>(WcfConfigExtension.MessageInspectorKey)
                    .ToList();
                dispatchMessageInspectors.Count.ShouldEqual(2);
                dispatchMessageInspectors.Contains(customMessage).ShouldBeTrue();
                dispatchMessageInspectors.Contains(customMessageMethod).ShouldBeTrue();

                List<IServiceBehavior> serviceBehaviors = injector
                    .GetAll<IServiceBehavior>(WcfConfigExtension.ServiceBehaviorKey)
                    .ToList();
                serviceBehaviors.Count.ShouldEqual(2);
                serviceBehaviors.Contains(customBehavior).ShouldBeTrue();
                serviceBehaviors.Contains(customBehaviorMethod).ShouldBeTrue();

                //Release instance behavior
                TestUtils.Catch<BindingNotFoundException>(
                    () => injector.Get<IReleaseInstanceBehavior>(WcfConfigExtension.ReleaseInstanceKey));

                IReleaseInstanceBehavior releaseInstance = new CustomReleaseInstanceBehavior();
                applicationConfiguration.WithReleaseInstanceBehavior(syntax => syntax.ToConstant(releaseInstance));
                var releaseInstanceBehavior =
                    injector.Get<IReleaseInstanceBehavior>(WcfConfigExtension.ReleaseInstanceKey);
                releaseInstance.ShouldEqual(releaseInstanceBehavior);

                //Dispose release instance behavior
                applicationConfiguration.WithDisposeReleaseInstanceBehavior();
                releaseInstanceBehavior = injector.Get<IReleaseInstanceBehavior>(WcfConfigExtension.ReleaseInstanceKey);
                releaseInstanceBehavior.ShouldBeType<DisposeReleaseInstanceBehavior>();
            }
        }

        [TestMethod]
        public void DetectSingletonTest()
        {
            WcfConfigExtension.IsServiceSingleton(typeof(SingletonService)).ShouldBeTrue();
            WcfConfigExtension.IsServiceSingleton(typeof(PerCallService)).ShouldBeFalse();
            WcfConfigExtension.IsServiceSingleton(typeof(PerSessionService)).ShouldBeFalse();
            WcfConfigExtension.IsServiceSingleton(typeof(EmptyService)).ShouldBeFalse();
        }
    }

    public class CustomMessageInspector : IDispatchMessageInspector
    {
        #region Implementation of IDispatchMessageInspector

        /// <summary>
        /// Called after an inbound message has been received but before the message is dispatched to the intended operation.
        /// </summary>
        /// <returns>
        /// The object used to correlate state. This object is passed back in the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply(System.ServiceModel.Channels.Message@,System.Object)"/> method.
        /// </returns>
        /// <param name="request">The request message.</param><param name="channel">The incoming channel.</param><param name="instanceContext">The current service instance.</param>
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Called after the operation has returned but before the reply message is sent.
        /// </summary>
        /// <param name="reply">The reply message. This value is null if the operation is one way.</param><param name="correlationState">The correlation object returned from the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest(System.ServiceModel.Channels.Message@,System.ServiceModel.IClientChannel,System.ServiceModel.InstanceContext)"/> method.</param>
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    public class CustomServiceBehavior : IServiceBehavior
    {
        #region Implementation of IServiceBehavior

        /// <summary>
        /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param><param name="serviceHostBase">The service host that is currently being constructed.</param>
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Provides the ability to pass custom data to binding elements to support the contract implementation.
        /// </summary>
        /// <param name="serviceDescription">The service description of the service.</param><param name="serviceHostBase">The host of the service.</param><param name="endpoints">The service endpoints.</param><param name="bindingParameters">Custom objects to which binding elements have access.</param>
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
                                         Collection<ServiceEndpoint> endpoints,
                                         BindingParameterCollection bindingParameters)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param><param name="serviceHostBase">The host that is currently being built.</param>
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    public class CustomReleaseInstanceBehavior : IReleaseInstanceBehavior
    {
        #region Implementation of IReleaseInstanceBehavior

        /// <summary>
        /// Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
        /// </summary>
        /// <param name="instanceContext">The service's instance context.</param><param name="instance">The service object to be recycled.</param>
        public void Release(InstanceContext instanceContext, object instance)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class SingletonService
    {

    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class PerCallService { }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class PerSessionService { }

    public class EmptyService { }
}